package com.ant.common.utils;

import com.baomidou.mybatisplus.core.toolkit.Sequence;
import com.baomidou.mybatisplus.core.toolkit.StringPool;

import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 生成ID的工具类，通过mybatis plus抽取
 *
 * @author
 */
public class IdWorker {

    /**
     * 主机和进程的机器码
     */
    private static Sequence WORKER = new Sequence();

    /**
     * 毫秒格式化时间
     */
    public static final DateTimeFormatter MILLISECOND = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");

    /**
     * 秒格式化时间
     */
    public static final DateTimeFormatter SECOND = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");

    /**
     * 小时格式化时间
     */
    public static final DateTimeFormatter HOUR = DateTimeFormatter.ofPattern("MMddHH");

    /**
     * 天格式化时间
     */
    public static final DateTimeFormatter DAY = DateTimeFormatter.ofPattern("yyyyMMdd");

    /**
     * 雪花计算下一个id
     *
     * @return
     */
    public static long getId() {
        long nextId = WORKER.nextId();
        if (nextId <= 0) {
            //如果生成为0，则再生成一个，并增加一个1到3的随机数
            nextId = WORKER.nextId() + ThreadLocalRandom.current().nextLong(1, 3);
        }
        return nextId;
    }


    /**
     * 16位随机带大小写字母
     *
     * @return
     */
    public static String getId16() {
        return getIdByNum(16);
    }

    /**
     * 按num位数随机带大小写字母
     *
     * @param num
     * @return
     */
    public static String getIdByNum(int num) {
        StringBuilder sb = new StringBuilder();
        //产生16位的强随机数
        Random rd = new SecureRandom();
        for (int i = 0; i < num; i++) {
            //产生0-2的3位随机数
            int type = rd.nextInt(3);
            switch (type) {
                case 0:
                    //0-9的随机数
                    sb.append(rd.nextInt(10));
                    break;
                case 1:
                    //ASCII在65-90之间为大写,获取大写随机
                    sb.append((char) (rd.nextInt(25) + 65));
                    break;
                case 2:
                    //ASCII在97-122之间为小写，获取小写随机
                    sb.append((char) (rd.nextInt(25) + 97));
                    break;
                default:
                    break;
            }
        }

        return sb.toString();
    }

    /**
     * 按num随机数字，不带字母
     *
     * @param num
     * @return
     */
    public static String getNum(int num) {
        StringBuilder sb = new StringBuilder();
        Random rd = new SecureRandom();
        for (int i = 0; i < num; i++) {
            sb.append(rd.nextInt(10));
        }
        return sb.toString();
    }

    /**
     * 32位随机带字母
     *
     * @return
     */
    public static String getId32() {
        return getIdByNum(32);
    }

    /**
     * 64位随机带字母
     *
     * @return
     */
    public static String getId64() {
        return getIdByNum(64);
    }

    /**
     * 71位随机带字母
     *
     * @return
     */
    public static String getId71() {
        return getIdByNum(71);
    }

    /**
     * 8位随机带字母
     *
     * @return
     */
    public static String getId8() {
        return getIdByNum(8);
    }

    /**
     * 4位随机数字
     *
     * @return
     */
    public static String getNum4() {
        String current = String.valueOf(ThreadLocalRandom.current().nextLong(1, 9999));
        switch (current.length()) {
            case 4:
                break;
            case 3:
                current = current + String.valueOf(ThreadLocalRandom.current().nextLong(1, 9));
                break;
            case 2:
                current = String.valueOf(ThreadLocalRandom.current().nextLong(1, 9)) +
                        current + String.valueOf(ThreadLocalRandom.current().nextLong(1, 9));
                break;
            case 1:
                current = String.valueOf(ThreadLocalRandom.current().nextLong(1, 9)) +
                        String.valueOf(ThreadLocalRandom.current().nextLong(1, 9)) + current +
                        String.valueOf(ThreadLocalRandom.current().nextLong(1, 9));
                break;
            default:
                break;
        }
        return current;
    }

    /**
     * 18位数字随机
     *
     * @return
     */
    public static synchronized String getNumId18() {
        return getSecond() + getNum4();
    }


    public static String getIdStr() {
        //return String.valueOf(WORKER.nextId());
        return String.valueOf(getId());
    }

    /**
     * 格式化的毫秒时间
     */
    public static String getMillisecond() {
        return LocalDateTime.now().format(MILLISECOND);
    }

    /**
     * 格式化秒时间
     *
     * @return
     */
    public static String getSecond() {
        return LocalDateTime.now().format(SECOND);
    }

    /**
     * 格式化小时时间
     *
     * @return
     */
    public static String getHour() {
        return LocalDateTime.now().format(HOUR);
    }

    /**
     * 格式化天时间
     *
     * @return
     */
    public static String getDay() {
        return LocalDateTime.now().format(DAY);
    }

    /**
     * 时间 ID = Time + ID
     * 毫秒+雪花
     * <p>例如：可用于商品订单 ID</p>
     */
    public static String getTimeId() {
        return getMillisecond() + getId();
    }

    /**
     * 小时+雪花
     *
     * @return
     */
    public static String getHourId() {
        return getHour() + getId();
    }

    /**
     * 天+雪花
     *
     * @return
     */
    public static String getDayId() {
        return getDay() + getId();
    }

    /**
     * 有参构造器
     *
     * @param workerId     工作机器 ID
     * @param datacenterId 序列号
     */
    public static void initSequence(long workerId, long datacenterId) {
        WORKER = new Sequence(workerId, datacenterId);
    }

    /**
     * 使用ThreadLocalRandom获取UUID获取更优的效果 去掉"-"
     */
    public static String get32UUID() {
        ThreadLocalRandom random = ThreadLocalRandom.current();
        return new UUID(random.nextLong(), random.nextLong()).toString().replace(StringPool.DASH, StringPool.EMPTY);
    }

    public static void main(String[] args) {
        String numId18 = getNumId18();
        System.out.println(numId18);
    }

}
